
package com.hlkj.pay.service.pay.impl;
/*
 * Hlpay-Plus aggregate payment system.
 * Copyright (c) 2024-2025 Hlpay Team Copyright has the right of final interpretation.
 */

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;

import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.hlkj.framework.common.pojo.PageResult;
import com.hlkj.framework.common.util.collection.CollectionUtils;
import com.hlkj.framework.common.util.json.JsonUtils;
import com.hlkj.framework.mybatis.core.query.LambdaQueryWrapperX;
import com.hlkj.pay.common.constants.MqConstant;
import com.hlkj.pay.common.constants.RedisConstants;
import com.hlkj.pay.config.ExtRocketMQTemplate;
import com.hlkj.pay.dto.pay.PayChannelQueryDto;
import com.hlkj.pay.enums.PayEnums;
import com.hlkj.pay.infrastructure.mapper.pay.PayChannelMapper;
import com.hlkj.pay.infrastructure.mapper.pay.PayChannelRequestMapper;
import com.hlkj.pay.infrastructure.mapper.pay.ProviderChannelConfigMapper;
import com.hlkj.pay.infrastructure.model.pay.PayChannelDO;
import com.hlkj.pay.infrastructure.model.pay.PayChannelRequestDO;
import com.hlkj.pay.infrastructure.model.pay.ProviderChannelConfigDO;
import com.hlkj.pay.service.pay.IPayChannelService;

import cn.hutool.core.collection.CollUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;

/**
 * @author HlpayTeam
 * @date 2024/09/23 11:23
 */
@Service
@Slf4j
@RequiredArgsConstructor
public class PayChannelServiceImpl implements IPayChannelService {

    private final PayChannelMapper payChannelMapper;

    private final PayChannelRequestMapper payChannelRequestMapper;

    private final ExtRocketMQTemplate extRocketMQTemplate;

    private final ProviderChannelConfigMapper providerChannelConfigMapper;

    @Override
    public PageResult<PayChannelDO> queryPage(PayChannelQueryDto payWayQueryDO) {
        return payChannelMapper.selectPage(payWayQueryDO, buildWrapper(payWayQueryDO));
    }

    @Override
    public List<PayChannelDO> queryList(PayChannelQueryDto payWayQueryDO) {

        if(CollUtil.isNotEmpty(payWayQueryDO.getProviderCodes())){
            LambdaQueryWrapperX<ProviderChannelConfigDO> queryWrapper = new LambdaQueryWrapperX<>();
            queryWrapper.inIfPresent(ProviderChannelConfigDO::getProviderCode, payWayQueryDO.getProviderCodes());
            List<ProviderChannelConfigDO> providerChannelConfigDOList = providerChannelConfigMapper.selectList(queryWrapper);
            List<String> providerChannelCodeList = new ArrayList<>();
            if(CollUtil.isNotEmpty(providerChannelConfigDOList)){
                for(ProviderChannelConfigDO providerChannelConfigDO:providerChannelConfigDOList){
                    providerChannelCodeList.add(providerChannelConfigDO.getChannelCode());
                }
                if(CollUtil.isNotEmpty(providerChannelCodeList)){
                    // 去重
                    providerChannelCodeList = providerChannelCodeList.stream().distinct().collect(Collectors.toList());
                    payWayQueryDO.setCodes(providerChannelCodeList);
                }
            }
        }
        return payChannelMapper.selectList(buildWrapper(payWayQueryDO));
    }

    @Override
    public PayChannelDO detail(Long id) {
        return payChannelMapper.selectById(id);
    }

    @Override
    @Cacheable(value = RedisConstants.PAY_CHANNEL_INFO, key = "#code", unless = "#result == null")
    public PayChannelDO queryByCode(String code) {
        PayChannelQueryDto payWayQueryDO = new PayChannelQueryDto();
        payWayQueryDO.setCode(code);
        List<PayChannelDO> channelList = queryList(payWayQueryDO);
        if (CollectionUtils.isEmpty(channelList)) {
            return null;
        }
        return channelList.get(0);
    }

    @Override
    public List<PayChannelDO> queryByCodes(List<String> codes) {
        PayChannelQueryDto payWayQueryDO = new PayChannelQueryDto();
        payWayQueryDO.setCodes(codes);
        List<PayChannelDO> channelList = queryList(payWayQueryDO);
        if (CollectionUtils.isEmpty(channelList)) {
            return Collections.emptyList();
        }
        return channelList;
    }

    @Override
    public PayChannelDO queryByName(String name) {
        PayChannelQueryDto payWayQueryDO = new PayChannelQueryDto();
        payWayQueryDO.setName(name);
        List<PayChannelDO> channelList = queryList(payWayQueryDO);
        if (CollectionUtils.isEmpty(channelList)) {
            return null;
        }
        return channelList.get(0);
    }

    @Override
    public void save(PayChannelDO payChannelDO) {
        payChannelMapper.insert(payChannelDO);
    }

    @Override
    @CacheEvict(value = RedisConstants.PAY_CHANNEL_INFO, key = "#payChannelDO.code")
    public void update(PayChannelDO payChannelDO) {
        payChannelMapper.updateById(payChannelDO);
    }

    @Override
    @CacheEvict(value = RedisConstants.PAY_CHANNEL_INFO, key = "#code")
    public void del(String code) {
        PayChannelQueryDto payChannelQueryDto = new PayChannelQueryDto();
        payChannelQueryDto.setCode(code);
        payChannelMapper.delete(buildWrapper(payChannelQueryDto));
    }

    @Override
    @CacheEvict(value = RedisConstants.PAY_CHANNEL_INFO, key = "#code")
    public void updatePayWayStatus(String code, Integer status) {
        PayChannelDO payChannelDO = new PayChannelDO();
        PayChannelQueryDto payChannelQueryDto = new PayChannelQueryDto();
        payChannelQueryDto.setCode(code);
        payChannelDO.setStatus(status);
        payChannelMapper.update(payChannelDO, buildWrapper(payChannelQueryDto));
    }

    @Override
    public void saveRequest(PayChannelRequestDO payChannelRequestDO, PayEnums.CHANNEL_REQUEST_TYPE channelRequestType) {
        payChannelRequestDO.setId(null);
        payChannelRequestDO.setType(channelRequestType.getCode());
        String jsonString = JsonUtils.toJsonString(payChannelRequestDO);
        log.info("sendMerchantRequestMessage:{}", jsonString);
        try {
            extRocketMQTemplate.send(MqConstant.MERCHANT_REQUEST_LOG_TOPIC, payChannelRequestDO);
        }catch (Exception e){
            log.error("sendMerchantRequestMessage error:{}", e.getMessage());
        }
    }

    @Override
    public void saveRequestLocal(PayChannelRequestDO payChannelRequestDO, PayEnums.CHANNEL_REQUEST_TYPE channelRequestType) {
        payChannelRequestDO.setId(null);
        payChannelRequestDO.setType(channelRequestType.getCode());
        payChannelRequestMapper.insert(payChannelRequestDO);
    }

    @Override
    public void consumerRequest(List<PayChannelRequestDO> payChannelRequestList) {
        payChannelRequestMapper.insertBatch(payChannelRequestList);
    }

    @Override
    public PayChannelRequestDO queryRequestByRequestId(String requestId) {
        return payChannelRequestMapper.selectOne(PayChannelRequestDO::getRequestId, requestId);
    }

    @Override
    public PayChannelRequestDO queryRequest(PayChannelRequestDO payChannelRequestDO) {
        return payChannelRequestMapper.selectOne(buildRequestWrapper(payChannelRequestDO));
    }

    @Override
    public PayChannelRequestDO queryRequestByRelationId(String relationId, PayEnums.CHANNEL_REQUEST_TYPE channelRequestType) {
        return payChannelRequestMapper.selectOne(PayChannelRequestDO::getRelationId, relationId, PayChannelRequestDO::getType, channelRequestType.getCode());
    }

    @Override
    public PayChannelRequestDO queryRequestByAccessId(String requestId, String requestUrl) {
        return payChannelRequestMapper.selectOne(buildRequestWrapper(requestId, requestUrl));
    }

    @Override
    public void updateRequestStatus(Long id, Integer status) {
        PayChannelRequestDO payChannelRequestDO = new PayChannelRequestDO();
        payChannelRequestDO.setId(id);
        payChannelRequestDO.setStatus(status);
        payChannelRequestMapper.updateById(payChannelRequestDO);
    }

    /**
     * 查询条件封装
     * 
     * @param payChannelQueryDto
     * @return
     */
    LambdaQueryWrapper<PayChannelDO> buildWrapper(PayChannelQueryDto payChannelQueryDto) {
        LambdaQueryWrapperX<PayChannelDO> queryWrapper = new LambdaQueryWrapperX<>();
        queryWrapper.eqIfPresent(PayChannelDO::getCode, payChannelQueryDto.getCode());
        queryWrapper.inIfPresent(PayChannelDO::getCode, payChannelQueryDto.getCodes());
        queryWrapper.likeIfPresent(PayChannelDO::getCode, payChannelQueryDto.getLikeCode());
        queryWrapper.eqIfPresent(PayChannelDO::getName, payChannelQueryDto.getName());
        queryWrapper.likeIfPresent(PayChannelDO::getName, payChannelQueryDto.getLikeName());
        queryWrapper.eqIfPresent(PayChannelDO::getStatus, payChannelQueryDto.getStatus());
        queryWrapper.eqIfPresent(PayChannelDO::getApplyType, payChannelQueryDto.getApplyType());
        queryWrapper.eqIfPresent(PayChannelDO::getMerchantType, payChannelQueryDto.getMerchantType());
        queryWrapper.eqIfPresent(PayChannelDO::getSpecialMerchantType, payChannelQueryDto.getSpecialMerchantType());
        queryWrapper.eqIfPresent(PayChannelDO::getType, payChannelQueryDto.getType());
        return queryWrapper;
    }

    /**
     * 查询条件封装
     *
     * @param accessId
     * @return
     */
    LambdaQueryWrapper<PayChannelRequestDO> buildRequestWrapper(String relationId, String requestUrl) {
        LambdaQueryWrapperX<PayChannelRequestDO> queryWrapper = new LambdaQueryWrapperX<>();
        queryWrapper.eqIfPresent(PayChannelRequestDO::getRelationId, relationId);
        queryWrapper.eqIfPresent(PayChannelRequestDO::getRequestUrl, requestUrl);
        queryWrapper.orderByDesc(PayChannelRequestDO::getId);
        queryWrapper.last("limit 1");
        return queryWrapper;
    }


    LambdaQueryWrapper<PayChannelRequestDO> buildRequestWrapper(PayChannelRequestDO payChannelRequestDO) {
        LambdaQueryWrapperX<PayChannelRequestDO> queryWrapper = new LambdaQueryWrapperX<>();
        queryWrapper.eqIfPresent(PayChannelRequestDO::getRequestId, payChannelRequestDO.getRequestId());
        queryWrapper.eqIfPresent(PayChannelRequestDO::getRelationId, payChannelRequestDO.getRelationId());
        queryWrapper.eqIfPresent(PayChannelRequestDO::getRequestUrl, payChannelRequestDO.getRequestUrl());
        queryWrapper.orderByDesc(PayChannelRequestDO::getId);
        queryWrapper.last("limit 1");
        return queryWrapper;
    }

}
